package com.clp.protocol.iec104.iec104.client;

import com.clp.protocol.core.exception.FailedToSendFrameException;
import com.clp.protocol.core.pdu.PduSendCallback;
import com.clp.protocol.iec104.iec104.apdu.*;
import com.clp.protocol.iec104.iec104.client.async.DefaultMasterPromise;
import com.clp.protocol.iec104.iec104.client.async.MasterFuture;
import com.clp.protocol.iec104.iec104.client.async.MasterFutureListener;
import com.clp.protocol.iec104.iec104.client.async.MasterPromise;
import com.clp.protocol.iec104.iec104.client.async.sendapdu.*;
import com.clp.protocol.iec104.iec104.client.state.*;
import com.clp.protocol.iec104.iec104.common.res.DefaultRes;
import com.clp.protocol.iec104.iec104.common.res.SendTestFrVRes;
import com.clp.protocol.iec104.iec104.definition.Tc;
import com.clp.protocol.iec104.iec104.definition.quatype.TaQuaType;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;

import java.util.Collection;
import java.util.Vector;

public class InMasterApduSender implements MasterApduSender {
    private final InMaster inMaster;
    protected final Vector<PduSendCallback<Apdu>> frameSendCallbacks = new Vector<>();

    public InMasterApduSender(InMaster inMaster) {
        this.inMaster = inMaster;
    }

    @Override
    public void addSendCallback(PduSendCallback<Apdu> callback) {
        frameSendCallbacks.add(callback);
    }

    @Override
    public void addSendCallbacks(Collection<PduSendCallback<Apdu>> frameSendCallbacks) {
        this.frameSendCallbacks.addAll(frameSendCallbacks);
    }

    @Override
    public void removeSendCallback(PduSendCallback<Apdu> callback) {
        frameSendCallbacks.remove(callback);
    }

    @Override
    public void removeSendCallbacks(Collection<PduSendCallback<Apdu>> frameSendCallbacks) {
        this.frameSendCallbacks.removeAll(frameSendCallbacks);
    }

    @Override
    public MasterFuture<DefaultRes> sendUApduOfTestFrC() {
        DefaultMasterPromise<DefaultRes> sendPromise = new DefaultMasterPromise<>(inMaster, new DefaultRes());
        UApdu uApdu = ApduFactory.getUApduOfTestFrC();
        send(uApdu, sendPromise).addListener((ChannelFutureListener) future -> {
            if (future.isSuccess()) {
                sendPromise.getRes().setSendSuccess(true);
                sendPromise.setSuccess();
                return;
            }
            sendPromise.getRes().setSendSuccess(false);
            sendPromise.setFailure(new FailedToSendFrameException(uApdu));
        });
        return sendPromise;
    }

    @Override
    public MasterFuture<DefaultRes> sendSApduOfAck() {
        DefaultMasterPromise<DefaultRes> sendPromise = new DefaultMasterPromise<>(inMaster, new DefaultRes());
        SApdu sApdu = ApduFactory.getSApdu(inMaster.recvSeq());
        send(sApdu, sendPromise).addListener((ChannelFutureListener) future -> {
            if (future.isSuccess()) {
                sendPromise.getRes().setSendSuccess(true);
                sendPromise.setSuccess();
                return;
            }
            sendPromise.getRes().setSendSuccess(false);
            sendPromise.setFailure(new FailedToSendFrameException(sApdu));
        });
        return sendPromise;
    }

    @Override
    public MasterFuture<SendTestFrVRes> sendUApduOfTestFrV() {
        DefaultMasterPromise<SendTestFrVRes> sendPromise = new DefaultMasterPromise<>(inMaster, new SendTestFrVRes());
        UApdu uApdu = ApduFactory.getUApduOfTestFrV();
        if (inMaster.stateInfo().isTestingFr()) {
            sendPromise.getRes().setSendSuccess(false).setRecvAckSuccess(false);
            sendPromise.setFailure(new FailedToSendFrameException(uApdu));
            return sendPromise;
        }

        // 注册
        inMaster.stateInfo().getState(T0T1T2T3MasterState.class).register(sendPromise);
        // 发送
        send(uApdu, sendPromise).addListener((ChannelFutureListener) future -> {
            if (future.isSuccess()) {
                sendPromise.getRes().setSendSuccess(true);
                return;
            }
            sendPromise.getRes().setSendSuccess(false).setRecvAckSuccess(false);
            sendPromise.setFailure(new FailedToSendFrameException(uApdu));
        });
        return sendPromise;
    }

    @Override
    public MasterFuture<SendStartDtVRes> sendUApduOfStartDtV() {
        UApdu uApdu = ApduFactory.getUApduOfStartDtV();
        MasterPromise<SendStartDtVRes> sendPromise = new DefaultMasterPromise<>(inMaster, new SendStartDtVRes());
        if (inMaster.stateInfo().isStartedDt()) {
            sendPromise.getRes().setSendSuccess(false).setRecvAck(false);
            sendPromise.setFailure(new FailedToSendFrameException(uApdu));
            return sendPromise;
        }
        // 将 这个Promise注册到对应的状态中
        inMaster.stateInfo().getState(DtMasterState.class).register(sendPromise);
        send(uApdu, sendPromise);

        return sendPromise;
    }

    @Override
    public MasterFuture<SendTotalCall100Res> sendIApduOfTotalCall100() {
        IApdu iApdu = ApduFactory.getIApduOfTotalCall100(inMaster.sendSeq(), inMaster.recvSeq(), inMaster.rtuAddr());
        MasterPromise<SendTotalCall100Res> sendPromise = new DefaultMasterPromise<>(inMaster, new SendTotalCall100Res());
        if (!inMaster.stateInfo().isRecvedInitEnd() || inMaster.stateInfo().isDoingTotalCall100()) {
            sendPromise.getRes().setSendSuccess(false).setRecvTotalCall100Ack(false).setRecvTotalCall100End(false);
            sendPromise.setFailure(new FailedToSendFrameException(iApdu));
            return sendPromise;
        }
        // 将 这个Promise注册到对应的状态中
        inMaster.stateInfo().getState(TotalCall100MasterState.class).register(sendPromise);
        send(iApdu, sendPromise);
        return sendPromise;
    }

    @Override
    public MasterFuture<SendTotalCall101Res> sendIApduOfTotalCall101() {
        IApdu iApdu = ApduFactory.getIApduOfTotalCall101(inMaster.sendSeq(), inMaster.recvSeq(), inMaster.rtuAddr());
        MasterPromise<SendTotalCall101Res> sendPromise = new DefaultMasterPromise<>(inMaster, new SendTotalCall101Res());
        if (!inMaster.stateInfo().isRecvedInitEnd() || inMaster.stateInfo().isDoingTotalCall101()) {
            sendPromise.getRes().setSendSuccess(false).setRecvTotalCall101Ack(false).setRecvTotalCall101End(false);
            sendPromise.setFailure(new FailedToSendFrameException(iApdu));
            return sendPromise;
        }
        // 将 这个Promise注册到对应的状态中
        inMaster.stateInfo().getState(TotalCall101MasterState.class).register(sendPromise);
        send(iApdu, sendPromise);
        return sendPromise;
    }

    @Override
    public MasterFuture<SendTcSelectRes> sendIApduOfOnePointTcSelect(int infoObjAddr, boolean isSwitchOn) {
        Apdu iApdu = ApduFactory.getIApduOfOnePointTc(inMaster.sendSeq(), inMaster.recvSeq(), inMaster.rtuAddr(),
                infoObjAddr, Tc.CmdType.SELECT, isSwitchOn ? Tc.OnePointSwitch.ON : Tc.OnePointSwitch.OFF);
        MasterPromise<SendTcSelectRes> sendPromise = new SendTcSelectMasterPromise(inMaster, new SendTcSelectRes(), inMaster.rtuAddr(), infoObjAddr);
        if (!inMaster.stateInfo().isRecvedInitEnd() || inMaster.stateInfo().isDoingTcSelect(infoObjAddr)) {
            sendPromise.getRes().setSendSelectSuccess(false).setRecvSelectAckYes(false).setRecvSelectAckNo(false).setFailDesc("该测点正在进行遥控");
            sendPromise.setFailure(new FailedToSendFrameException(iApdu));
            return sendPromise;
        }
        // 注册
        inMaster.stateInfo().getState(TcMasterState.class).register(sendPromise);
        send(iApdu, sendPromise);
        return sendPromise;
    }

    @Override
    public MasterFuture<SendTcSelectRes> sendIApduOfTwoPointTcSelect(int infoObjAddr, boolean isSwitchOn) {
        Apdu iApdu = ApduFactory.getIApduOfTwoPointTc(inMaster.sendSeq(), inMaster.recvSeq(), inMaster.rtuAddr(),
                infoObjAddr, Tc.CmdType.SELECT, isSwitchOn ? Tc.TwoPointSwitch.ON : Tc.TwoPointSwitch.OFF);
        MasterPromise<SendTcSelectRes> sendPromise = new SendTcSelectMasterPromise(inMaster, new SendTcSelectRes(), inMaster.rtuAddr(), infoObjAddr);
        if (!inMaster.stateInfo().isRecvedInitEnd() || inMaster.stateInfo().isDoingTcSelect(infoObjAddr)) {
            sendPromise.getRes().setSendSelectSuccess(false).setRecvSelectAckYes(false).setRecvSelectAckNo(false).setFailDesc("该测点正在进行遥控");
            sendPromise.setFailure(new FailedToSendFrameException(iApdu));
            return sendPromise;
        }
        // 注册
        inMaster.stateInfo().getState(TcMasterState.class).register(sendPromise);
        send(iApdu, sendPromise);
        return sendPromise;
    }

    @Override
    public MasterFuture<SendTcExecuteRes> sendIApduOfOnePointTcExecute(int infoObjAddr, boolean isSwitchOn) {
        Apdu iApdu = ApduFactory.getIApduOfOnePointTc(inMaster.sendSeq(), inMaster.recvSeq(), inMaster.rtuAddr(),
                infoObjAddr, Tc.CmdType.EXECUTE, isSwitchOn ? Tc.OnePointSwitch.ON : Tc.OnePointSwitch.OFF);
        MasterPromise<SendTcExecuteRes> sendPromise = new SendTcExecuteMasterPromise(inMaster, new SendTcExecuteRes(), inMaster.rtuAddr(), infoObjAddr);
        if (!inMaster.stateInfo().isRecvedInitEnd() || inMaster.stateInfo().isDoingTcExecute(infoObjAddr)) {
            sendPromise.getRes().setSendExecuteSuccess(false).setRecvExecuteAckYes(false).setRecvExecuteAckNo(false).setRecvEnd(false)
                    .setFailDesc("该测点正在进行遥控");
            sendPromise.setFailure(new FailedToSendFrameException(iApdu));
            return sendPromise;
        }
        // 注册
        inMaster.stateInfo().getState(TcMasterState.class).register(sendPromise);
        send(iApdu, sendPromise);
        return sendPromise;
    }

    @Override
    public MasterFuture<SendTcExecuteRes> sendIApduOfTwoPointTcExecute(int infoObjAddr, boolean isSwitchOn) {
        Apdu iApdu = ApduFactory.getIApduOfTwoPointTc(inMaster.sendSeq(), inMaster.recvSeq(), inMaster.rtuAddr(),
                infoObjAddr, Tc.CmdType.EXECUTE, isSwitchOn ? Tc.TwoPointSwitch.ON : Tc.TwoPointSwitch.OFF);
        MasterPromise<SendTcExecuteRes> sendPromise = new SendTcExecuteMasterPromise(inMaster, new SendTcExecuteRes(), inMaster.rtuAddr(), infoObjAddr);
        if (!inMaster.stateInfo().isRecvedInitEnd() || inMaster.stateInfo().isDoingTcExecute(infoObjAddr)) {
            sendPromise.getRes().setSendExecuteSuccess(false).setRecvExecuteAckYes(false).setRecvExecuteAckNo(false).setRecvEnd(false)
                    .setFailDesc("该测点正在进行遥控");
            sendPromise.setFailure(new FailedToSendFrameException(iApdu));
            return sendPromise;
        }
        // 注册
        inMaster.stateInfo().getState(TcMasterState.class).register(sendPromise);
        send(iApdu, sendPromise);
        return sendPromise;
    }

    @Override
    public MasterFuture<SendTaSelectRes> sendIApduOfTaSelectNormalized(int infoObjAddr, int setVal) {
        Apdu iApdu = ApduFactory.getIApduOfTaNormalized(inMaster.sendSeq(), inMaster.recvSeq(), inMaster.rtuAddr(), infoObjAddr, setVal, TaQuaType.SELECT);
        MasterPromise<SendTaSelectRes> sendPromise = new SendTaSelectMasterPromise(inMaster, new SendTaSelectRes(), inMaster.rtuAddr(), infoObjAddr);
        if (!inMaster.stateInfo().isRecvedInitEnd() || inMaster.stateInfo().isDoingTaSelect(infoObjAddr)) {
            sendPromise.getRes().setSendSelectSuccess(false).setRecvSelectAckYes(false).setRecvSelectAckNo(false).setFailDesc("该测点正在进行遥调");
            sendPromise.setFailure(new FailedToSendFrameException(iApdu));
        }
        // 注册
        inMaster.stateInfo().getState(TaMasterState.class).register(sendPromise);
        send(iApdu, sendPromise);
        return sendPromise;
    }

    @Override
    public MasterFuture<SendTaExecuteRes> sendIApduOfTaExecuteNormalized(int infoObjAddr, int setVal) {
        Apdu iApdu = ApduFactory.getIApduOfTaNormalized(inMaster.sendSeq(), inMaster.recvSeq(), inMaster.rtuAddr(), infoObjAddr, setVal, TaQuaType.EXECUTE);
        MasterPromise<SendTaExecuteRes> sendPromise = new SendTaExecuteMasterPromise(inMaster, new SendTaExecuteRes(), inMaster.rtuAddr(), infoObjAddr);
        if (!inMaster.stateInfo().isRecvedInitEnd() || inMaster.stateInfo().isDoingTaExecute(infoObjAddr)) {
            sendPromise.getRes().setSendExecuteSuccess(false).setRecvExecuteAckYes(false).setRecvExecuteAckNo(false).setFailDesc("该测点正在进行遥调");
            sendPromise.setFailure(new FailedToSendFrameException(iApdu));
        }
        // 注册
        inMaster.stateInfo().getState(TaMasterState.class).register(sendPromise);
        send(iApdu, sendPromise);
        return sendPromise;
    }

    @Override
    public MasterFuture<SendTaSelectRes> sendIApduOfTaSelectFloat(int infoObjAddr, float setVal) {
        Apdu iApdu = ApduFactory.getIApduOfTaFloat(inMaster.sendSeq(), inMaster.recvSeq(), inMaster.rtuAddr(), infoObjAddr, setVal, TaQuaType.SELECT);
        MasterPromise<SendTaSelectRes> sendPromise = new SendTaSelectMasterPromise(inMaster, new SendTaSelectRes(), inMaster.rtuAddr(), infoObjAddr);
        if (!inMaster.stateInfo().isRecvedInitEnd() || inMaster.stateInfo().isDoingTaSelect(infoObjAddr)) {
            sendPromise.getRes().setSendSelectSuccess(false).setRecvSelectAckYes(false).setRecvSelectAckNo(false).setFailDesc("该测点正在进行遥调");
            sendPromise.setFailure(new FailedToSendFrameException(iApdu));
        }
        // 注册
        inMaster.stateInfo().getState(TaMasterState.class).register(sendPromise);
        send(iApdu, sendPromise);
        return sendPromise;
    }

    @Override
    public MasterFuture<SendTaExecuteRes> sendIApduOfTaExecuteFloat(int infoObjAddr, float setVal) {
        Apdu iApdu = ApduFactory.getIApduOfTaFloat(inMaster.sendSeq(), inMaster.recvSeq(), inMaster.rtuAddr(), infoObjAddr, setVal, TaQuaType.EXECUTE);
        MasterPromise<SendTaExecuteRes> sendPromise = new SendTaExecuteMasterPromise(inMaster, new SendTaExecuteRes(), inMaster.rtuAddr(), infoObjAddr);
        if (!inMaster.stateInfo().isRecvedInitEnd() || inMaster.stateInfo().isDoingTaExecute(infoObjAddr)) {
            sendPromise.getRes().setSendExecuteSuccess(false).setRecvExecuteAckYes(false).setRecvExecuteAckNo(false).setFailDesc("该测点正在进行遥调");
            sendPromise.setFailure(new FailedToSendFrameException(iApdu));
        }
        // 注册
        inMaster.stateInfo().getState(TaMasterState.class).register(sendPromise);
        send(iApdu, sendPromise);
        return sendPromise;
    }

    private <T> ChannelFuture send(Apdu apdu, MasterPromise<T> sendPromise) {
        frameSendCallbacks.forEach(apduFrameSendCallback -> apduFrameSendCallback.beforeSend(apdu));
        sendPromise.addListener(new MasterFutureListener<T>() {
            @Override
            public void operationComplete(MasterFuture<T> future) {
                if (future.isSuccess()) {
                    frameSendCallbacks.forEach(apduFrameSendCallback -> apduFrameSendCallback.afterSendSuccess(apdu));
                } else {
                    frameSendCallbacks.forEach(apduFrameSendCallback -> apduFrameSendCallback.afterSendFailed(apdu, future.cause()));
                }
            }
        });
        inMaster.getSendHandler().handleSending(apdu);
        return inMaster.channel().writeAndFlush(apdu).addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                if (!future.isSuccess()) {
                    sendPromise.setFailure(future.cause());
                }
            }
        });
    }
}
